/*
 * protocol_server.c
 *
 *  Created on: May 4, 2011
 *      Author: onteria_
 */

#include <stdint.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/socket.h>
#include <stdio.h>

#include <miko/defines.h>
#include <miko/miko.h>
#include <miko/socket.h>
#include <miko/protocol.h>

#include <unicode/ustdio.h>

/**
 * Function: miko_read_data
 *
 * Reads the length of a Miko packet into 32 bit integer
 *
 * Parameters:
 *
 *      socket_descriptor - The file descriptor of the socket
 *      data - A pointer to the data that will be read in
 *      length - The size of the data. This is set to uint32_t since the protocol uses it for packet length
 *      timeout - How long before the request times out
 *
 * Returns:
 *
 *      MIKO_SUCCESS on success, MIKO_TIMEOUT or MIKO_POLLERROR on failure
 */
int miko_read_data(int socket_descriptor, void * data, uint32_t length, int timeout) {
    long bytes_read;

    bytes_read = miko_read_socket(socket_descriptor, data, length, timeout);
    if(bytes_read > 0) {
    	return (enum miko_error) MIKO_SUCCESS;
    }
    else {
    	return bytes_read;
    }
}

/**
 * Function: process_miko_data
 *
 * A function to read a miko structure from a socket, with some error handling included
 *
 * Parameters:
 *
 *      socket_descriptor - The file descriptor of the client socket
 *      miko - A pointer to the Miko structure to populate
 *      timeout - The timeout in seconds to wait
 *
 * Returns:
 *
 *      MIKO_SUCCESS or a MIKO_ error related to socket communication or invalid data
 */
int process_miko_data(int socket_descriptor, struct Miko * miko, int timeout) {
	uint32_t length;
	long bytes_read;

	UChar first_name[MIKO_FIRST_NAME_MAX];
	UChar last_name[MIKO_LAST_NAME_MAX];
	UChar shrine_name[MIKO_SHRINE_NAME_MAX];

	bytes_read = miko_read_socket(socket_descriptor, &length, sizeof(uint32_t), timeout);
	if(bytes_read <= 0) {
		return bytes_read;
	}
	length = ntohl(length);

	/* Now we sanity check the length against our limits */
	if(length !=
			(sizeof(UChar) * MIKO_FIRST_NAME_MAX) +
			(sizeof(UChar) * MIKO_LAST_NAME_MAX) +
			(sizeof(UChar) * MIKO_SHRINE_NAME_MAX)
	) {
		return MIKO_DATA_CHECK_FAILURE;
	}

	bytes_read = miko_read_unicode_data(socket_descriptor, miko->firstName, first_name, MIKO_FIRST_NAME_MAX, timeout);
	if(bytes_read <= 0) {
		return bytes_read;
	}

	bytes_read = miko_read_unicode_data(socket_descriptor, miko->lastName, last_name, MIKO_LAST_NAME_MAX, timeout);
	if(bytes_read <= 0) {
		return bytes_read;
	}

	bytes_read = miko_read_unicode_data(socket_descriptor, miko->shrine, shrine_name, MIKO_SHRINE_NAME_MAX, timeout);
	if(bytes_read <= 0) {
		return bytes_read;
	}

	return MIKO_SUCCESS;
}

/**
 * Function: miko_read_unicode_data
 *
 * This function takes a temp buffer and validates it before copying it to the destination.
 * It uses miko_validate_string to make sure someone isn't passing weird things in. Note that
 * Invalid characters are potentially replaced with the unknown character symbol instead.
 *
 * Parameters:
 *
 *      socket_descriptor - The socket file descriptor to read the unicode data in from
 *      dest - The destination to copy the unicode data to on success
 *      buffer - The temporary buffer to hold potentially invalid data in
 *      size - The number of unicode characters which will be multiplied against sizeof(UChar)
 *      timeout - The number of seconds to wait until a timeout occurs
 *
 * Returns:
 *
 *      The number of bytes read or a MIKO_ error indicating socket issues or unicode parsing issues
 */
int miko_read_unicode_data(int socket_descriptor, UChar *dest, UChar *buffer, uint32_t size, int timeout) {
	long bytes_read;

	bzero(buffer, sizeof(UChar) * size);
	bytes_read = miko_read_socket(socket_descriptor, buffer, sizeof(UChar) * size, timeout);
	if(bytes_read <= 0) {
		return bytes_read;
	}
	else if(miko_validate_string(buffer, size) == MIKO_ERROR) {
		return MIKO_INVALID_UNICODE;
	}
	memcpy(dest, buffer, sizeof(UChar) * size);

	return bytes_read;
}

/**
 * Function: miko_validate_string
 *
 * Takes a string plus a max number of characters and validates that it is well-formed unicode. It uses
 * u_strToUTF8 from ICU's <unicode/ustring.h> to do a dry run and validate.
 *
 * Parameters:
 *
 *      string - The unicode string to check the validity of
 *      max_length - The number of unicode characters which will be multiplied against sizeof(UChar)
 *
 * Returns:
 *
 *      MIKO_SUCCESS for a valid string, MIKO_ERROR for an invalid string
 */
int miko_validate_string(UChar *string, uint32_t max_length) {
	UErrorCode errorCode;
	int32_t destLength;
	enum miko_error status;

	/*
	 * Okay so basically what this does is do a dry run on converting the string to utf8.
	 * This WILL produce an error in errorCode, set to U_BUFFER_OVERFLOW_ERROR. Logic behind it
	 * is that no buffer is specified. Because of this we ignore the overflow error and check
	 * for any other error instead.
	 */
	u_strToUTF8( NULL, 0, &destLength, string, sizeof(UChar) * max_length, &errorCode);
	if(U_FAILURE(errorCode) && errorCode != U_BUFFER_OVERFLOW_ERROR) {
		status = MIKO_ERROR;
	}
	else {
		status = MIKO_SUCCESS;
	}

	return status;
}

/**
 * Function: print_uchar_string
 *
 * Print a unicode string to standard output using the ICO ustdio function u_fprintf
 *
 * Parameters:
 *
 *      string - The unicode string to print
 *
 * Returns:
 *
 *      No return
 */
void print_uchar_string(UChar * string) {
	UFILE * out;
	out = u_finit(stdout, NULL, NULL);
	u_fprintf(out, "%S\n", string);
	u_fclose(out);
}

/**
 * Function: miko_send_response
 *
 * Write a server response to the socket
 *
 * Parameters:
 *
 *      socket_descriptor - The file descriptor of the socket
 *      response_code - The response code to retrieve
 *      message - A human readable message describing the response
 *
 * Returns:
 *
 *      MIKO_SUCCESS on success, MIKO_SEND_FAILED or MIKO_MSG_TOO_LONG on failure
 */
int miko_send_response(int socket_descriptor, int response_code, char * message) {
	int bytes_sent;
	uint32_t packet_size;

	if(strlen(message) > MIKO_MSG_LENGTH) {
		return (enum miko_error) MIKO_MSG_TOO_LONG;
	}

	packet_size = sizeof(response_code) + (sizeof(char) * MIKO_MSG_LENGTH);

	bytes_sent = send(socket_descriptor, &packet_size, sizeof(uint32_t), 0);
	if(bytes_sent < 0) {
		return (enum miko_error) MIKO_SEND_FAILED;
	}

	bytes_sent = send(socket_descriptor, &response_code, sizeof(int), 0);
	if(bytes_sent < 0) {
		return (enum miko_error) MIKO_SEND_FAILED;
	}

	bytes_sent = send(socket_descriptor, message, sizeof(char) * MIKO_MSG_LENGTH, 0);
	if(bytes_sent < 0) {
		return (enum miko_error) MIKO_SEND_FAILED;
	}
	else {
		return (enum miko_error) MIKO_SUCCESS;
	}
}
